package jnpf.util;

import jnpf.univer.chart.UniverChartField;
import jnpf.univer.data.cell.UniverDataConfig;
import jnpf.ureport.definition.value.AggregateType;
import jnpf.ureport.definition.value.DatasetValue;
import jnpf.ureport.utils.DataUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * @author ：JNPF开发平台组
 * @version: V3.1.0
 * @copyright 引迈信息技术有限公司
 * @date ：2024/9/10 下午3:18
 */
public class ChartUtil {

    public static UniverChartField chart(List<Map<String, Object>> dataList, UniverDataConfig dataConfig) {
        String classifyName = dataConfig.getClassifyNameField();
        String seriesName = dataConfig.getSeriesNameField();
        String seriesData = dataConfig.getSeriesDataField();
        List<Object> categoryList = new ArrayList<>();
        Map<Object, Map<Object, List<Object>>> seriesDataMap = new LinkedHashMap<>();
        for (int i = 0; i < dataList.size(); i++) {
            Map<String, Object> data = dataList.get(i);
            Object category = data.get(classifyName);
            if (category == null) {
                continue;
            }
            if (!categoryList.contains(category)) {
                categoryList.add(category);
            }
            Object series = data.get(seriesName) != null ? data.get(seriesName) : "";
            Object value = data.get(seriesData);
            if (value == null) {
                continue;
            }

            if (seriesDataMap.containsKey(series)) {
                Map<Object, List<Object>> categoryMap = seriesDataMap.get(series);
                List<Object> valueList = categoryMap.get(category) != null ? categoryMap.get(category) : new ArrayList<>();
                valueList.add(value);
                categoryMap.put(category, valueList);
            } else {
                Map<Object, List<Object>> categoryMap = new LinkedHashMap<>();
                seriesDataMap.put(series, categoryMap);
                for (Object cg : categoryList) {
                    categoryMap.put(cg, new ArrayList<>());
                }
                List<Object> valueList = categoryMap.get(category);
                valueList.add(value);
            }
        }
        return chartData(seriesDataMap, dataConfig);
    }

    private static UniverChartField chartData(Map<Object, Map<Object, List<Object>>> seriesDataMap, UniverDataConfig dataConfig) {
        UniverChartField chartField = new UniverChartField();
        List<String> seriesNameList = new ArrayList<>();
        Map<String, List<List<String>>> categoryMap = new HashMap<>();
        List<Integer> maxCount = new ArrayList<>();
        maxCount.add(0);
        for (Object series : seriesDataMap.keySet()) {
            seriesNameList.add(String.valueOf(series));
            Map<Object, List<Object>> classifyNameMap = seriesDataMap.get(series);
            for (Object category : classifyNameMap.keySet()) {
                List<List<String>> categroyList = new ArrayList<>();
                if (categoryMap.get(String.valueOf(category)) != null) {
                    categroyList.addAll(categoryMap.get(String.valueOf(category)));
                }
                List<Object> valueList = classifyNameMap.get(category);
                List<String> data = data(valueList, dataConfig);
                for (int i = 0; i < data.size(); i++) {
                    List<String> categoryData = new ArrayList<>();
                    categoryData.add(data.get(i));
                    categroyList.add(categoryData);
                }
                categoryMap.put(String.valueOf(category), categroyList);
                maxCount.add(categroyList.size());
            }
        }
        if (StringUtil.isNotEmpty(dataConfig.getSeriesNameField())) {
            chartField.setSeriesNameField(seriesNameList);
        }
        List<String> classifyNameList = new ArrayList<>(categoryMap.keySet());
        List<List<String>> seriesDataList = new ArrayList<>();
        for (int i = 0; i < Collections.max(maxCount); i++) {
            List<String> seriesData = new ArrayList<>();
            for (int k = 0; k < classifyNameList.size(); k++) {
                String category = classifyNameList.get(k);
                List<List<String>> categoryList = categoryMap.get(category) != null ? categoryMap.get(category) : new ArrayList<>();
                List<String> categoryData = categoryList.size() - 1 >= i ? categoryList.get(i) : new ArrayList<>();
                String data = categoryData.size() > 0 ? categoryData.get(0) : "";
                seriesData.add(data);
            }
            seriesDataList.add(seriesData);
        }
        chartField.setClassifyNameField(classifyNameList);
        chartField.setSeriesDataField(seriesDataList);
        return chartField;
    }

    private static List<String> data(List<Object> list, UniverDataConfig dataConfig) {
        List<String> result = new ArrayList<>();
        if (list.isEmpty()) {
            return result;
        }
        String chartType = AggregateType.value(dataConfig.getSummaryType()) != null ? dataConfig.getSummaryType() : AggregateType.select.name();
        DatasetValue expr = new DatasetValue();
        expr.setProperty(chartType);
        AggregateType value = AggregateType.value(chartType);
        List<Map<String, Object>> dataList = new ArrayList<>();
        for (Object data : list) {
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put(chartType, data);
            dataList.add(dataMap);
        }
        List<BigDecimal> bindDataList = DataUtils.dataList(expr, dataList);
        switch (value) {
            case sum:
                result.add(bindDataList.stream().reduce(BigDecimal.ZERO, BigDecimal::add).doubleValue() + "");
                break;
            case avg:
                BigDecimal sumDecimal = bindDataList.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
                result.add(!list.isEmpty() ? sumDecimal.divide(new BigDecimal(list.size()), 8, BigDecimal.ROUND_HALF_UP).doubleValue() + "" : "0");
                break;
            case max:
                if (list.isEmpty()) {
                    bindDataList.add(new BigDecimal(0));
                }
                result.add(bindDataList.stream().reduce(bindDataList.get(0), BigDecimal::max).doubleValue() + "");
                break;
            case min:
                if (list.isEmpty()) {
                    bindDataList.add(new BigDecimal(0));
                }
                result.add(bindDataList.stream().reduce(bindDataList.get(0), BigDecimal::min).doubleValue() + "");
                break;
            case count:
                result.add(list.size() + "");
                break;
            default:
                for (Object data : list) {
                    result.add(String.valueOf(data));
                }
                break;
        }
        return result;
    }

}
